package Model.Main;

import Model.Inputs.*;
import Model.Gates.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A worksheet tárolja el az elemeket. Megvalósítja az mvc modellben a
 * modell szerepét.
 ** @author citromos ezustfoka
 */
public class Worksheet {

    private ArrayList<Gate> Gates;
    private ArrayList<Input> Inputs;
    private static final Logger logger =
            Logger.getLogger(Worksheet.class.getName());

    /**
     * konstruktor
     * @return
     */
    public Worksheet() {
        logger.finest("Worksheet created.");
        Gates = new ArrayList<Gate>();
        Inputs = new ArrayList<Input>();
    }

    /**
     *A bemeneti elemek sendvalue függvényének meghívása.
     * @return
     * 
     */
    public void runInputsOnce() throws Exception {
        // tesztelem az inputokat, hogy wire-dek e?
        logger.finest("Testing all gate inputs for wires");
        Boolean wiredTest = true;
        for (Gate g : Gates) {
            wiredTest = wiredTest && g.testAllInputsWired();
        }
        // ha valamelyik nem wired, azonnal ikszepsönt dobunk
        if (wiredTest == false) {
            throw new Exception("Some imputs are not wired.");
        }
        logger.config("----->Calling inputs once to send values:");
        Input input;
        for (int i = 0; i < Inputs.size(); i++) {
            input = Inputs.get(i);
            logger.log(Level.FINEST, "Calling " + input.getClass().getName() + " " + input.getName() + " to send value.");
           
                input.sendState();
           
            }
        
        logger.config("----->Run input once completed.");

    }

    /**
     *Minden törlése. Array listek ürítése.
     * @return
     * 
     */
    public void clear() {
        logger.log(Level.CONFIG, "Creating new worksheet lists. (Deleting all contents)");
        Gates = new ArrayList<Gate>();
        Inputs = new ArrayList<Input>();
    }

    /**
     *Minden alaphelyzetbe állít.
     * ezt meg kéne írni nem?
     * @return
     * 
     */
    public void reset() {
        throw new UnsupportedOperationException("Not yet implemented.");
    }

    /**
     *Input hozzáadása.
     * @param i Input amit hozzáadunk az arraylisthez.
     * @return
     * 
     */
    public void addInput(Input i) {
        logger.log(Level.FINEST, "Adding " + i.getName() + "to worksheet.");
        Inputs.add(i);
    }

    /**
     *Gates hozzáadása.
     * @param g Gate amit hozzáadunk az arraylisthez.
     * @return
     * 
     */
    public void addGate(Gate g) {
        logger.log(Level.FINEST, "Adding {0} to worksheet.",
                g);
        Gates.add(g);
    }

    /**
     * Composite hozzáadása
     * @param c
     */
    public void addComposite(Composite c) {
        logger.log(Level.FINEST, "Adding {0} to worksheet", c);
        Gates.add(c);
    }

    /**
     * Kaput ad a composite-joz
     * @param c
     */
    public void addGateComposite(Composite c, Gate g) {
        logger.log(Level.FINEST, "Adding {0} to {1}", new Object[]{g, c});
        c.addGate(g);
    }

    /**
     *Az adott button értékét állítja be.
     * @param onInput  Az az Input példány referenciája, aminek az értékét szeretnénk beállítani.  
     * @param state   A bemenet értéke
     * @return
     * 
     */
    public void setState(Input onInput, Boolean state) {
        logger.log(Level.CONFIG, "Setting state state {0} on {1}",
                new Object[]{state, onInput});
        onInput.setState(state);
    }

    /**
     *Inputs és Gates osztályt összekötő vezeték hozzáadása.
     * @param from  Az Input aminek a kimenetére állítjuk a wire-t
     * @param to A Gate aminek a bemenetére állítjuk a wire-t
     * @return
     * @throws kivételt dob, ha nem sikerült rákötni a kapu bmeneti lábára a kábeltot.
     */
    public void addWire(Input from, Gate to, int toInput, String name) throws Exception {
        logger.log(Level.CONFIG, "Adding wire between " + from.getClass().getName() + " " + from.getName() + " and " + to.getClass().getName() + " " + to.getName() + " on input " + toInput + ".");
        // ez a rész azt teszteli, hogy lehet-e rátenni
        // az adott kapu adott bemenetére wire-t. ha lehet, akkor a
        // gate-n ezt jelezni fogjuk, hogy ott van egy wire.
        to.addWireToInput(toInput);
        // ha ez rendbe ment, akkor rá lehet kötni 
        // a bemenetre a kábelt, létrehozzuk azt
        Wire w = new Wire(to, toInput, name);
        from.setOutputWire(w);
    }

    /**
     *Gates és Gates osztályt összekötő vezeték hozzáadása.
     * @param from  A Gate aminek a kimenetére állítjuk a wire-t
     * @param to A Gate aminek a bemenetére állítjuk a wire-t
     * @return
     * @throws kivételt dob, ha nem sikerült rákötni a kapu bemeneti lábára a kábeltot.
     */
    public void addWire(Gate from, int fromOutput, Gate to, int toInput, String name) throws Exception {
        logger.log(Level.CONFIG, "Adding wire between " + from.getClass().getName() + " " + from.getName() + " and " + to.getClass().getName() + " " + to.getName() + " on input " + toInput + ".");
        // ez a rész azt teszteli, hogy lehet-e rátenni
        // az adott kapu adott bemenetére wire-t. ha lehet, akkor a
        // gate-n ezt jelezni fogjuk, hogy ott van egy wire.
        to.addWireToInput(toInput);
        // ha ez rendbe ment, akkor rá lehet kötni 
        // a bemenetre a kábelt, létrehozzuk azt
        Wire w = new Wire(to, toInput, name);
        from.setOutputWire(w, fromOutput);
    }

    /**
     * A kompozit addwire függvényét hívja meg.
     * A kompozit bemenetét köti össze egy belső kapu bemenetével.
     * @param fromInput
     * @param to
     * @param toInput
     */
    public void addWire(Composite comp, int fromInput, Gate to, int toInput, String name) throws Exception {
        // ez a rész azt teszteli, hogy lehet-e rátenni
        // az adott kapu adott bemenetére wire-t. ha lehet, akkor a
        // gate-n ezt jelezni fogjuk, hogy ott van egy wire.
        to.addWireToInput(toInput);
        // ha ez rendbe ment, akkor rá lehet kötni
        // a bemenetre a kábelt, létrehozzuk azt
        comp.addWire(fromInput, to, toInput, name);
        logger.log(Level.CONFIG, "Adding wire between " + comp.getClass().getName() + " " + comp.getName() + " from input " + fromInput + " and " + to.getClass().getName() + " " + to.getName() + " on input " + toInput + ".");
    }

    /**
     * A kompozit addwire függvényét hívja meg.
     * Egy belső kapu kimenetét köti össze a kompozit kimenetével.
     * @param from
     * @param outputNumber
     * @param toInput
     */
    public void addWire(Composite comp, Gate from, int outputNumber, int toOutput, String name) throws Exception {
        /**
         * Itt nincs mit ellenőrizni, a composite megcsinálja azt.
         */
        comp.addWire(from, outputNumber, toOutput, name);
        logger.log(Level.CONFIG, "Adding wire between " + from.getClass().getName() + " " + from.getName() + " and " + comp.getClass().getName() + " " + comp.getName() + " to output " + toOutput + ".");
    }

    /**
     *Kölyök Wire
     *
     * @param from Wire amire rá lesz kötve a wire
     * @param to a Gate amire rá akarjuk kötni
     * @param toInput A Gate bemenetének száma
     * @param name a Wire neve
     * @return
     * 
     */
    public void addchildWire(Wire child, Wire from, Gate to, int toInput, String name) throws Exception {
        to.addWireToInput(toInput);

        from.addChildWire(child);

    }

    /**
     *Inputs és Gates osztályt összekötő vezeték törlése.
     *
     * @param from az Input, amiről ki van kötve a wire
     * @param name a Wire neve
     * @return
     * 
     */
    public void delWire(Input from, String name) throws Exception {
        // ha nem egyezik meg a törölni kívánt wire neve az inputon lévő wire
        // nevével akkor hibát dobunk!
        if (!from.getWire().getName().equals(name)) {
            throw new Exception("There is no '" + name + "' wire atteched to " + from.getName());
        }
        logger.log(Level.CONFIG, "Delete wire " + name + " between " + from.getName() + " and " + from.getWire().getGate().getName());
        // letöröljük a wire jelző bitet a kapuról ahová be volt kötve
        // mostantól ott nincs wire. ez dobhat kivételt, akkor
        // valami hiba van, de nem itt.
        from.getWire().getGate().removeWireFromInput(from.getWire().getWhichInput());
        //childwire-ket is töröljük..
        for (int i = 0; i < from.getWire().getChildNumber(); i++) {
            from.getWire().getChildWire(i).getGate().removeWireFromInput(from.getWire().getChildWire(i).getWhichInput());
        }
        // miután kitöröböltük a jelzőbitet, a from-ról null-t állítunk
        // kimeneti wire-re.
        from.setOutputWire(null);
    }

    /**
     *Gates és Gates osztályt összekötő vezeték törlése.
     * @param from  A Gate aminek a kimenetéről töröljük a wire-t
     * @param fromOutput a Gate melyik (int) outputjáról töröljük a wire-t
     * @param name a wire neve (ellenőrzés céljából)
     * @return
     * 
     */
    public void delWire(Gate from, int fromOutput, String name) throws Exception {
        // ha nem egyezik meg a törölni kívánt wire neve az inputon lévő wire
        // nevével akkor hibát dobunk!
        if (!from.getWire(fromOutput).getName().equals(name)) {
            throw new Exception("There is no '" + name + "' wire atteched to " + from.getName());
        }
        logger.log(Level.CONFIG, "Delete wire " + name + " between " + from.getName() + " and " + from.getWire(fromOutput).getGate().getName());
        // letöröljük a wire jelző bitet a kapuról ahová be volt kötve
        // mostantól ott nincs wire. ez dobhat kivételt, hogy nem is volt akkor
        // valami hiba van, de nem itt.
        from.getWire(fromOutput).getGate().removeWireFromInput(from.getWire(fromOutput).getWhichInput());
        //childwire-ket is töröljük..
        for (int i = 0; i < from.getWire(fromOutput).getChildNumber(); i++) {
            from.getWire(fromOutput).getChildWire(i).getGate().removeWireFromInput(from.getWire(fromOutput).getChildWire(i).getWhichInput());
        }
        // miután kitöröböltük a jelzőbitet, a from-ról null-t állítunk
        // kimeneti wire-re.
        from.setOutputWire(null, fromOutput);
    }

    public void delGate(Gate g) throws Exception {
        if (!Gates.contains(g)) {
            throw new Exception("delGate: There is no such gate in this worksheet.");
        }
        logger.log(Level.CONFIG, "delete Gate " + g.getName() + " from " + this.getClass().getName() + " ");
        Gates.remove(g);
    }

    public void delInput(Input i) throws Exception {
        if (!Inputs.contains(i)) {
            throw new Exception("delInput: There is no such Input in this worksheet");
        }
        logger.log(Level.CONFIG, "delete Input " + i.getName() + " from " + this.getClass().getName() + " ");
        Inputs.remove(i);
    }
}
